package cn.touna.jss.modules.third.geo.util;

import cn.touna.jss.common.util.CommonUtils;
import cn.touna.jss.common.util.HttpClientUtils;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.*;

/**
 * HTTP请求工具类
 *
 * @author XUZIMING 2016-10-11
 */
public class HttpUtils {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    /**
     * 使用connection.setRequestProperty写入头消息
     *
     * @param connection
     * @param headerMap
     */
    public static void fillHeaderMap(URLConnection connection, Map<String, String> headerMap) {
        if (connection == null || headerMap == null)
            return;
        Iterator<Map.Entry<String, String>> iter = headerMap.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> entry = iter.next();
            connection.setRequestProperty(entry.getKey(), entry.getValue());
        }
    }

    public static String queryParams(Map<String, String> params) {
        if (params == null)
            return "";
        StringBuilder sb = new StringBuilder();
        Iterator<Map.Entry<String, String>> iter = params.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> entry = iter.next();
            sb.append(entry.getKey()).append("=").append(entry.getValue() == null ? "" : URLEncoder.encode(entry.getValue())).append("&");
        }
        if (sb.length() > 0)
            sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }

    /**
     * 从流中读取文本，并关闭流
     * <br/>异常时，返回空字符串
     *
     * @param inputStream
     * @param charSet
     * @return
     */
    public static String readText(InputStream inputStream, String charSet) {
        StringBuilder result = new StringBuilder();
        BufferedReader br = null;
        try {
            if (org.springframework.util.StringUtils.isEmpty(charSet))
                br = new BufferedReader(new InputStreamReader(inputStream));
            else
                br = new BufferedReader(new InputStreamReader(inputStream, charSet));
            String line = null;
            while ((line = br.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            logger.error("error to send rest request", e);
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    logger.error(e.getMessage());
                }
            }
        }
        return result.toString();
    }

    /***
     * JSON POST请求
     * @param httpUrl
     * @param jsonData
     * @return
     */
    public static String jsonPost(String httpUrl, String jsonData) {
        byte[] data = jsonData.getBytes();
        InputStream instr = null;
        DataOutputStream printout = null;
        HttpURLConnection urlCon = null;

        try {
            URL url = new URL(httpUrl);
            urlCon = (HttpURLConnection) url.openConnection();
            urlCon.setDoOutput(true);
            urlCon.setDoInput(true);
            urlCon.setUseCaches(false);
            urlCon.setConnectTimeout(5000);
            urlCon.setReadTimeout(5000);
            urlCon.setRequestMethod("POST");
            urlCon.setRequestProperty("Content-Type", "text/xml");
            urlCon.setRequestProperty("Content-length", String.valueOf(data.length));
            printout = new DataOutputStream(urlCon.getOutputStream());
            printout.write(data);
            printout.flush();
            printout.close();
            instr = urlCon.getInputStream();
            byte[] bis = IOUtils.toByteArray(instr);
            String resp = new String(bis, CommonUtils.UTF8);
            return resp;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                urlCon.disconnect();
                instr.close();
                printout.close();
            } catch (Exception ex) {
                return "";
            }
        }
    }

    /**
     * 向指定URL发送POST方法的请求
     *
     * @param url      发送请求的 URL
     * @param paramMap 参数Map
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, Map<String, Object> paramMap) {
        String params = parseUrlParamsByMap(paramMap);
        return sendPost(url, params);
    }

    /**
     * 向指定URL发送POST方法的请求
     *
     * @param url    发送请求的 URL
     * @param params 请求参数(请求参数形式: name1=value1&name2=value2)
     * @return 所代表远程资源的响应结果
     */
    private static String sendPost(String url, String params, Map<String, String> requestPropertyMap) {
        logger.info("=== sendPost, url: {}, params: {}", new Object[]{url, params});

        PrintWriter out = null;
        String result = new String();

        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            HttpUtils.fillHeaderMap(conn, requestPropertyMap);

            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);

            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());

            // 发送请求参数
            out.print(params);
            // flush输出流的缓冲
            out.flush();
            // 关闭输出流
            out.close();

            // 读取默认编码的返回信息
            // result = IOUtils.toString(conn.getInputStream(), CommonUtils.UTF8);
            result = readContentByCharset(conn.getInputStream(), CommonUtils.UTF8);
        } catch (Exception e) {
            logger.error("=== sendPost, remote invoke error!");
            logger.error(e.getMessage(), e);
        } finally {// 使用finally块来关闭输出流、输入流
            if (out != null) out.close();
        }

        logger.info("=== sendPost, response: {}", result);
        return result;
    }

    /**
     * 向指定URL发送POST方法的请求
     *
     * @param url    发送请求的 URL
     * @param params 请求参数(请求参数形式: name1=value1&name2=value2)
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String params) {
        Map<String, String> requestPropertyMap = buildRequestProperties();
        return sendPost(url, params, requestPropertyMap);
    }

    private static Map<String, String> buildRequestProperties() {
        Map<String, String> requestPropertyMap = new HashMap<String, String>();

        // 设置通用的请求属性
        requestPropertyMap.put("accept", "*/*");
        requestPropertyMap.put("Accept-Charset", CommonUtils.UTF8);
        requestPropertyMap.put("contentType", CommonUtils.UTF8);
        requestPropertyMap.put("Content-type", "application/x-www-form-urlencoded;charset=" + CommonUtils.UTF8);
        requestPropertyMap.put("Accept-Language", Locale.getDefault().toString());
        String[] acceptType = {
                "image/gif", "image/jpeg", "image/pjpeg", "image/pjpeg",
                "application/x-shockwave-flash", "application/xaml+xml",
                "application/vnd.ms-xpsdocument", "application/x-ms-xbap",
                "application/x-ms-application", "application/vnd.ms-excel",
                "application/vnd.ms-powerpoint", "application/msword", "*/*"};
        requestPropertyMap.put("Accept", StringUtils.join(acceptType, ","));
        requestPropertyMap.put("connection", "Keep-Alive");
        requestPropertyMap.put("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

        return requestPropertyMap;
    }

    /**
     * 根据指定字符集编码读取流数据
     *
     * @param in          输入流
     * @param charsetName 字符集编码
     * @return 对应的字符集结果内容
     */
    public static String readContentByCharset(InputStream in, String charsetName) {
        StringBuilder builder = new StringBuilder();
        BufferedReader reader = null;
        String line = null;
        try {
            reader = new BufferedReader(new InputStreamReader(in, charsetName));
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            try {
                reader.close();
            } catch (IOException ioe) {
                logger.error(ioe.getMessage(), ioe);
            }
        }

        return builder.toString();
    }

    /**
     * 将map转换成HTTP参数
     *
     * @param paramMap 参数Map
     * @return 参数字符串(参数形式: name1=value1&name2=value2)
     */
    public static String parseUrlParamsByMap(Map<String, Object> paramMap) {
        if (paramMap == null || paramMap.isEmpty()) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
            builder.append(entry.getKey() + "=" + entry.getValue());
            builder.append("&");
        }

        String params = builder.toString();
        if (params.endsWith("&")) {
            params = StringUtils.substringBeforeLast(params, "&");
        }
        return params;
    }

    /**
     * 上传文件
     *
     * @param url           请求地址
     * @param file          待上传文件
     * @param fileFieldName 上传文件的文件域名称(不写则默认取待上传文件的文件名)
     * @throws ClientProtocolException 请求上传地址出错
     * @throws IOException             关闭流资源出错
     */
    public static String uploadFile(String url, Map<String, Object> paramMap,
                                    File file, String fileFieldName) throws ClientProtocolException, IOException {
        // 创建可关闭的HTTP客户端对象
        CloseableHttpClient httpclient = HttpClients.createDefault();

        if (paramMap != null) {
            String params = parseUrlParamsByMap(paramMap);
            url += "?" + params;
        }

        HttpPost httppost = new HttpPost(url);
        try {
            FileBody fileBody = new FileBody(file);
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

            String name = CommonUtils.isEmptyContainNull(fileFieldName) ? file.getName() : fileFieldName;
            HttpEntity reqEntity = builder.addPart(name, fileBody).build();

            httppost.setEntity(reqEntity);
            logger.info("executing request " + httppost.getRequestLine());

            CloseableHttpResponse response = httpclient.execute(httppost);
            logger.info(ObjectUtils.toString(response.getStatusLine()));

            try {
                HttpEntity respHttpEntity = response.getEntity();
                if (respHttpEntity != null) {
                    return EntityUtils.toString(respHttpEntity, Charset.forName(CommonUtils.UTF8));
                }
                EntityUtils.consume(respHttpEntity);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }

        return null;
    }

    /**
     * 读取网络文件流(适用于中小文件)
     *
     * @param url      网络地址
     * @param paramMap 请求参数
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     * @throws Exception
     */
    public static final byte[] doPost(String url,
                                      Map<String, Object> paramMap) throws ClientProtocolException, IOException {
        // 创建可关闭的HTTP客户端对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        if (paramMap != null && !paramMap.isEmpty()) {

            for (String name : paramMap.keySet()) {
                String value = ObjectUtils.toString(paramMap.get(name));
                nameValuePairs.add(new BasicNameValuePair(name, value));
            }
        }
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            HttpEntity entity = response.getEntity();
            InputStream in = entity.getContent();
            byte[] data = IOUtils.toByteArray(in);
            if (data != null) return data;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            response.close();
            httpClient.close();
        }

        return null;
    }

    public static void main(String[] args) throws Exception {
        String url = "https://www.juxinli.com/orgApi/rest/v2/messages/collect/req";
        try {
            JSONObject dataGather = new JSONObject();
            dataGather.put("token", "6988e655b1a84e21a7b3dea70f2dcbef");
            dataGather.put("account", "18210622782");
            dataGather.put("password", "127009");
            dataGather.put("captcha", "493661");
            dataGather.put("type", "SUBMIT_CAPTCHA");
            dataGather.put("website", "chinamobilebj");
            //数据采集
//			Map<String, String> headerMap = new HashMap<>();
            long start = System.currentTimeMillis();
            //String result = HttpsUtil.getInstance().requestJson(url, headerMap, dataGather);
            String result = HttpClientUtils.httpPost(url, dataGather.toString());
            long end = System.currentTimeMillis();
            System.out.println("返回：" + result + ",耗时：" + (end - start) + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
